package com.bestcem.xm.user.service.impl;

import cn.hutool.core.lang.Dict;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.enums.AuthErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ReqErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ValidationErrorCodeEnum;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.common.core.oss.enums.OssPathEnum;
import com.bestcem.xm.component.security.dto.TokenDTO;
import com.bestcem.xm.componet.filestorage.service.StorageService;
import com.bestcem.xm.user.constant.BusinessConstant;
import com.bestcem.xm.user.constant.ExcelConstant;
import com.bestcem.xm.user.dao.UserGroupDao;
import com.bestcem.xm.user.entity.pojo.UserGroupDO;
import com.bestcem.xm.user.enums.*;
import com.bestcem.xm.user.handler.GroupImportHandler;
import com.bestcem.xm.user.handler.GroupSheetHandler;
import com.bestcem.xm.user.service.*;
import com.bestcem.xm.user.service.dto.group.*;
import com.bestcem.xm.user.service.dto.user.OrganizationDTO;
import com.bestcem.xm.user.util.business.AsyncUtil;
import com.bestcem.xm.user.util.business.ExcelUtil;
import com.bestcem.xm.user.util.business.GroupLevelUtil;
import com.bestcem.xm.user.util.business.GroupLockUtil;
import com.bestcem.xm.user.util.convert.UserGroupConvert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 用户组织层级导入/导出service
 *
 * @author wangyunyun <yunyun.wang@idiaoyan.com>
 * @version v1.0
 * @date 2022/2/10 17:47
 */
@Slf4j
@Service
public class UserGroupImportServiceImpl implements UserGroupImportService {

    @Resource
    private UserGroupConvert userGroupConvert;

    @Autowired
    private UserGroupService userGroupService;

    @Resource
    private UserGroupDao userGroupDao;

    @Autowired
    private UserUserGroupService userUserGroupService;

    @Autowired
    private GroupImportHandler groupImportHandler;

    @Autowired
    private OrganizationServiceImpl organizationService;

    @Autowired
    private UserGroupImportRecordService userGroupImportRecordService;

    @Autowired
    private UserGroupExportRecordService userGroupExportRecordService;

    @Autowired
    private UserGroupLevelService userGroupLevelService;

    /*@Resource
    private OssService ossService;*/

    @Resource
    private GroupLockUtil groupLockUtil;

    @Autowired
    private StorageService storageService;

    @Override
    public ServiceResult<String> uploadUserGroup(MultipartFile excel, TokenDTO tokenDTO) {
        // 加锁
        groupLockUtil.lock(tokenDTO.getOrgId());
        ServiceResult<String> handleResult = null;
        try {
            handleResult = this.handleGroupImport(excel, tokenDTO);
            if (!handleResult.isSuccess()
                    && !String.valueOf(ResourceErrorCodeEnum.TASK_WORKING.getCode()).equals(handleResult.getErrorCode())) {
                // 解锁
                groupLockUtil.unlock(tokenDTO.getOrgId());
            }
        } catch (Throwable e) {
            log.error("组织导入失败", e);
            // 解锁
            groupLockUtil.unlock(tokenDTO.getOrgId());
        }
        return handleResult;
    }

    @Override
    public ServiceResult<String> downloadUserGroup(TokenDTO tokenDTO, HttpServletResponse response) {
        // 创建组织导出记录
        UserGroupExportRecordDTO exportRecordDTO = new UserGroupExportRecordDTO();
        exportRecordDTO.setOrgId(tokenDTO.getOrgId());
        exportRecordDTO.setCreatorId(tokenDTO.getUserId());
        exportRecordDTO.setOperatorId(tokenDTO.getUserId());
        exportRecordDTO.setStatus(GroupExportRecordStatusEnum.ON_HAND.getStatus());
        ServiceResult<String> insertRt = userGroupExportRecordService.insert(exportRecordDTO);
        exportRecordDTO.setExportId(insertRt.getData());

        handleGroupExport(exportRecordDTO, response);
        // 提交任务
        //AsyncUtil.EXPORT_GROUP_THREAD_POOL_EXECUTOR.execute(() -> handleGroupExport(exportRecordDTO, response));

        return ServiceResult.success(insertRt.getData());
    }

    @Override
    public ServiceResult<?> checkUploadLevel(TokenDTO tokenDTO) {
        UserGroupDTO rootGroup = null;
        // 发送新增组织消息
        UserGroupDO userGroupDO = userGroupDao.getRootGroupByOrgId(tokenDTO.getOrgId());

        if (Objects.nonNull(userGroupDO)) {
            rootGroup = userGroupConvert.doToDto(userGroupDO);
        }
        // 校验用户是否权限: 通过公司根组织信息是否存在以及用户的角色和所属组织
        boolean hasPermission = checkUploadPermission(tokenDTO, rootGroup);
        if (!hasPermission) {
            return ServiceResult.fail(ResourceErrorCodeEnum.PERMISSION_DENY, "no permission");
        }
        return ServiceResult.success();
    }

    /**
     * 处理组织上传文件
     *
     * @param excel    上传文件
     * @param tokenDTO 用户认证信息
     * @return ServiceResult<String>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 16:45
     */
    private ServiceResult<String> handleGroupImport(MultipartFile excel, TokenDTO tokenDTO) {
        // 通过组织导入记录校验组织层级是否可导入
        ServiceResult<Boolean> checkImportActiveRt = userGroupImportRecordService.checkImportActive(tokenDTO.getOrgId());
        if (!checkImportActiveRt.isSuccess()) {
            return ServiceResult.fail(checkImportActiveRt.getErrorCode(), checkImportActiveRt.getMsg());
        }
        if (Boolean.FALSE.equals(checkImportActiveRt.getData())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.TASK_WORKING, "组织层级有未完成的全量更新任务，请稍后再试");
        }
        // 通过公司id获取公司信息; rootGroupId
        ServiceResult<OrganizationDTO> organizationDTOServiceResult = organizationService.selectByPrimaryKey(tokenDTO.getOrgId());
        if (!organizationDTOServiceResult.isSuccess() || Objects.isNull(organizationDTOServiceResult.getData())) {
            return ServiceResult.failWithCode(AuthErrorCodeEnum.ORG_NOT_EXISTS);
        }
        OrganizationDTO organizationDTO = organizationDTOServiceResult.getData();
        // 通过接口返回的公司信息,得到根组织id,然后查询并校验根组织信息
        UserGroupDTO rootGroup = null;
        if (StringUtils.isNotBlank(organizationDTO.getRootGroupId())) {
            ServiceResult<UserGroupDTO> rootGroupResult = userGroupService.getByIdAndOrgId(organizationDTO.getRootGroupId(), tokenDTO.getOrgId());
            if (!rootGroupResult.isSuccess()) {
                return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "Group root node not found");
            }
            rootGroup = rootGroupResult.getData();
        }
        // 校验用户是否权限: 通过公司根组织信息是否存在以及用户的角色和所属组织
        boolean hasPermission = checkUploadPermission(tokenDTO, rootGroup);
        if (!hasPermission) {
            return ServiceResult.fail(ResourceErrorCodeEnum.PERMISSION_DENY, "no permission");
        }

        // 校验参数
        if (excel == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "excel file is lost");
        }
        // 校验excel文件格式
        String fileName = excel.getOriginalFilename();
        if (StringUtils.isBlank(fileName) || !(fileName.toLowerCase().endsWith(".xlsx") || fileName.toLowerCase().endsWith(".xls"))) {
            return ServiceResult.fail(ValidationErrorCodeEnum.FILE_TYPE_NOT_RIGHT, "filetype not allowed");
        }
        // 上传文件
        String uploadFileName = String.format(ExcelConstant.USER_GROUP_DOWNLOAD_NAME,
                StringUtils.isNotBlank(organizationDTO.getName()) ? organizationDTO.getName() : organizationDTO.getOrgId());
        //String ossUrl = ossService.uploadFile(excel.getBytes(), uploadFileName, OssPathEnum.USER, "导入用户组织数据");
        //String ossUrl = storageService.upload(excel.getBytes(), uploadFileName, OssPathEnum.USER.getPath(), new Dict()).getUrl();
        String ossUrl = storageService.upload(excel, uploadFileName, OssPathEnum.USER.getPath(), new Dict()).getUrl();

        // 保存待处理的组织导入记录
        long fileSize = excel.getSize() / 1024;
        UserGroupImportRecordDTO importRecordDTO = new UserGroupImportRecordDTO();
        importRecordDTO.setImportFileName(fileName);
        importRecordDTO.setImportFileSize(String.valueOf(fileSize));
        importRecordDTO.setOrgId(tokenDTO.getOrgId());
        importRecordDTO.setStatus(GroupImportRecordStatusEnum.ON_HAND.getStatus());
        importRecordDTO.setType(GroupImportRecordTypeEnum.MANAGER.getType());
        importRecordDTO.setImportFilePath(ossUrl);
        importRecordDTO.setCreatorId(tokenDTO.getUserId());
        importRecordDTO.setOperatorId(tokenDTO.getUserId());
        String importId = userGroupImportRecordService.insert(importRecordDTO).getData();
        importRecordDTO.setImportId(importId);

        // 向线程池提交任务
        this.getThreadPoolExecutor(fileSize).execute(() -> updateGroupByImportRecord(importRecordDTO));

        return ServiceResult.success(importId);
    }

    /**
     * 通过文件大小获取对应的线程池
     *
     * @param fileSize 文件大小 单位 k
     * @return ThreadPoolExecutor
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 14:31
     */
    private ThreadPoolExecutor getThreadPoolExecutor(long fileSize) {
        if (fileSize < 500) {
            return AsyncUtil.LITTLE_GROUP_THREAD_POOL_EXECUTOR;
        } else {
            return AsyncUtil.MUCH_GROUP_THREAD_POOL_EXECUTOR;
        }
    }

    /**
     * 校验用户是否有上传组织的权限
     *
     * @param tokenDTO  登陆认证dto
     * @param rootGroup 根组织
     * @return
     */
    private boolean checkUploadPermission(TokenDTO tokenDTO, UserGroupDTO rootGroup) {
        if (NumberUtils.INTEGER_ONE.equals(tokenDTO.getIsSenior()) || NumberUtils.INTEGER_ONE.equals(tokenDTO.getSuperRole())) {
            // 有无根组织的情况下, 管理员角色都可创建(管理员: 系统管理员/高级管理员)
            return true;
        } else if (rootGroup == null) {
            // 没有根组织的情况下: 只有管理员角色能够创建
            return false;
        } else {
            // 有根组织的情况下, 非管理员角色: 当前用户的所属组织需要包含根组织
            ServiceResult<List<UserUserGroupDTO>> serviceResult = userUserGroupService
                    .listByGroupIdAndUserId(rootGroup.getGroupId(), rootGroup.getGroupId(), tokenDTO.getUserId());

            return serviceResult.isSuccess() && CollectionUtils.isNotEmpty(serviceResult.getData());
        }
    }

    /**
     * 通过导入记录更新组织层级
     *
     * @param importRecordDTO 导入组织记录对象
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 14:34
     */
    private void updateGroupByImportRecord(UserGroupImportRecordDTO importRecordDTO) {
        // 设置参数
        TokenDTO tokenDTO = new TokenDTO();
        tokenDTO.setOrgId(importRecordDTO.getOrgId());
        tokenDTO.setUserId(importRecordDTO.getCreatorId());
        try {
            // 正在处理，更新状态为处理中
            userGroupImportRecordService.updateStatus(importRecordDTO.getOrgId(), importRecordDTO.getImportId(), GroupImportRecordStatusEnum.HANDING.getStatus());
            // 下载文件
            //InputStream inputStream = ossService.downloadByUrl(importRecordDTO.getImportFilePath());
            InputStream inputStream = new ByteArrayInputStream(storageService.downloadByUrl(importRecordDTO.getImportFilePath()).bytes());
            // 读取excel文件数据,校验文件内容格式, 保存组织层级
            ServiceResult<String> result = groupImportHandler.process(inputStream, tokenDTO);
            if (result.isSuccess()) {
                // 处理成功，更新状态为处理成功
                userGroupImportRecordService.updateStatus(importRecordDTO.getOrgId(), importRecordDTO.getImportId(), GroupImportRecordStatusEnum.SUCCESS_HAND.getStatus());
            } else {
                // 处理失败，更新状态为处理失败
                UserGroupImportRecordDTO updateImportRecord = new UserGroupImportRecordDTO();
                updateImportRecord.setStatus(GroupImportRecordStatusEnum.FAIL_HAND.getStatus());
                updateImportRecord.setErrorMsg(result.getMsg());
                updateImportRecord.setOrgId(importRecordDTO.getOrgId());
                updateImportRecord.setImportId(importRecordDTO.getImportId());
                if (StringUtils.isNotBlank(result.getData())) {
                    updateImportRecord.setErrorFilePath(result.getData());
                }
                userGroupImportRecordService.updateByIdAndOrgId(updateImportRecord);
            }

        } catch (Throwable e) {
            log.error("导入组织层级失败", e);
            // 处理失败，更新状态为处理失败
            UserGroupImportRecordDTO updateImportRecord = new UserGroupImportRecordDTO();
            updateImportRecord.setStatus(GroupImportRecordStatusEnum.FAIL_HAND.getStatus());
            updateImportRecord.setErrorMsg("未知异常, 导入组织层级失败");
            updateImportRecord.setOrgId(importRecordDTO.getOrgId());
            updateImportRecord.setImportId(importRecordDTO.getImportId());
            userGroupImportRecordService.updateByIdAndOrgId(updateImportRecord);
        }
        // 解锁
        groupLockUtil.unlock(tokenDTO.getOrgId());
    }

    /**
     * 处理组织下载
     *
     * @param exportRecordDTO 导出组织记录dto
     * @param response
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 18:30
     */
    private void handleGroupExport(UserGroupExportRecordDTO exportRecordDTO, HttpServletResponse response) {
        // 正在处理，更新状态为处理中
        userGroupExportRecordService.updateStatus(exportRecordDTO.getOrgId(), exportRecordDTO.getExportId(), GroupExportRecordStatusEnum.HANDING.getStatus());
        // 设置并上传层级组织导出文件
        ServiceResult<Map<String, String>> uploadResult;
        try {
            uploadResult = uploadUserGroupExcel(exportRecordDTO.getOrgId(), response);
        } catch (Throwable e) {
            log.error("处理组织下载失败", e);
            uploadResult = ServiceResult.fail("未知异常, 处理组织下载失败");
        }
        // 设置更新参数
        UserGroupExportRecordDTO updateExportRecord = new UserGroupExportRecordDTO();
        updateExportRecord.setExportId(exportRecordDTO.getExportId());
        updateExportRecord.setOrgId(exportRecordDTO.getOrgId());
        if (uploadResult.isSuccess()) {
            // 处理成功，更新状态为处理成功
            updateExportRecord.setStatus(GroupExportRecordStatusEnum.SUCCESS_HAND.getStatus());
            updateExportRecord.setExportFileName(uploadResult.getData().get("fileName"));
            updateExportRecord.setExportFilePath(uploadResult.getData().get("ossUrl"));
            updateExportRecord.setExportFileSize(uploadResult.getData().get("fileSize"));
        } else {
            // 处理失败，更新状态为处理失败
            updateExportRecord.setStatus(GroupExportRecordStatusEnum.FAIL_HAND.getStatus());
            updateExportRecord.setErrorMsg(uploadResult.getMsg());
        }
        // 更新组织导出记录
        userGroupExportRecordService.updateByIdAndOrgId(updateExportRecord);
    }

    /**
     * 设置并上传层级组织导出文件
     *
     * @param orgId    公司id
     * @param response
     * @return
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 14:34
     */
    private ServiceResult<Map<String, String>> uploadUserGroupExcel(String orgId, HttpServletResponse response) {
        // 请求接口: 通过公司id获取公司信息; 公司名称/rootGroupId,
        ServiceResult<OrganizationDTO> organizationDTOServiceResult = organizationService.selectByPrimaryKey(orgId);
        if (!organizationDTOServiceResult.isSuccess() || Objects.isNull(organizationDTOServiceResult.getData())) {
            return ServiceResult.failWithCode(AuthErrorCodeEnum.ORG_NOT_EXISTS);
        }
        OrganizationDTO organizationDTO = organizationDTOServiceResult.getData();

        // 通过接口返回的公司信息,得到根组织id,然后查询并校验根组织信息
        if (StringUtils.isBlank(organizationDTO.getRootGroupId())) {
            return ServiceResult.fail("该公司下不存在状态为有效的根组织");
        }
        ServiceResult<UserGroupDTO> rootGroupResult = userGroupService.getById(organizationDTO.getRootGroupId());
        if (!rootGroupResult.isSuccess()) {
            return ServiceResult.fail(null, rootGroupResult.getMsg(), rootGroupResult.getErrorCode());
        }
        UserGroupDTO rootGroup = rootGroupResult.getData();
        if (rootGroup == null || !UserGroupStatusEnum.ACTIVATED.getStatus().equals(rootGroup.getGroupStatus())) {
            return ServiceResult.fail("该公司下不存在状态为有效的根组织");
        }

        // 查询公司下的所有层级
        ServiceResult<List<UserGroupLevelDTO>> levelResults = userGroupLevelService.listByOrgId(orgId);
        if (!levelResults.isSuccess()) {
            return ServiceResult.fail(levelResults.getMsg());
        }
        if (CollectionUtils.isEmpty(levelResults.getData()) || levelResults.getData().size() < BusinessConstant.USER_LEVEL_MIN_COUNT) {
            return ServiceResult.fail(String.format("层级数至少有%d个", BusinessConstant.USER_LEVEL_MIN_COUNT));
        }

        // 查询公司下的所有组织
        ServiceResult<List<UserGroupDTO>> groupResults = userGroupService.listByOrgId(orgId);
        if (!groupResults.isSuccess()) {
            return ServiceResult.fail(groupResults.getMsg());
        }
        if (CollectionUtils.isEmpty(groupResults.getData())) {
            return ServiceResult.fail("组织数至少有一个");
        }

        // 设置根据层级数 排序后的层级集合
        List<UserGroupLevelDTO> levels = levelResults.getData()
                .stream()
                .sorted(Comparator.comparingInt(UserGroupLevelDTO::getLevelNum))
                .collect(Collectors.toList());
        // 设置根据用户组排序 排序后的用户组集合
        List<UserGroupDTO> groups = groupResults.getData()
                .stream()
                .filter(v -> v.getGroupStatus() != null
                        && (UserGroupStatusEnum.ACTIVATED.getStatus().equals(v.getGroupStatus())
                        || UserGroupStatusEnum.HIDDEN.getStatus().equals(v.getGroupStatus())))
                .sorted(Comparator.nullsLast(Comparator.comparing(UserGroupDTO::getGroupOrder, Comparator.nullsLast(Integer::compareTo))))
                .collect(Collectors.toList());

        // 设置导出组织集合
        List<UserGroupExportDTO> userGroupExports = buildExportGroups(groups, rootGroup);

        // 设置写入流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 设置要写入的excel对象
        ExcelWriter excelWriter = EasyExcelFactory.write(outputStream).build();
        // 设置填写须知sheet
        ExcelUtil.buildGroupIntroduceSheet(excelWriter);
        // 设置首页sheet
        writeIndexSheet(excelWriter, levels, userGroupExports);
        // 设置动态sheet
        writeDynamicSheet(excelWriter, levels, userGroupExports);
        // 设置Excel文件写数据结束
        excelWriter.finish();

        // 上传导出文件 设置文件名称：如果公司名称存在则templateName为公司名称,公司名称为空则为orgId
        String fileName = String.format(ExcelConstant.USER_GROUP_DOWNLOAD_NAME,
                StringUtils.isNotBlank(organizationDTO.getName()) ? organizationDTO.getName() : organizationDTO.getOrgId());
        //String ossUrl = ossService.uploadFile(outputStream.toByteArray(), fileName, OssPathEnum.USER, "导入用户组织数据");
        String ossUrl = storageService.upload(outputStream.toByteArray(), fileName, OssPathEnum.USER.getPath(), new Dict()).getUrl();
        int fileSize = outputStream.size() / 1024;

        try {
            // 这种方式mac和windows导出的文件名就不会乱码
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new BusinessException("编码异常：{}", e);
        }
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/octet-stream");
        response.addHeader("Content-Transfer-Encoding", "binary");
        try {
            response.getOutputStream().write(outputStream.toByteArray());
            // 关闭流
            outputStream.close();
        } catch (IOException e) {
            log.error("下载用户组织数据数据, 写入流/关闭流失败", e);
        }
        // 设置返回值
        Map<String, String> successData = new HashMap<>();
        successData.put("ossUrl", ossUrl);
        successData.put("fileName", fileName);
        successData.put("fileSize", String.valueOf(fileSize));
        return ServiceResult.success(successData);
    }

    /**
     * 设置导出组织集合
     *
     * @param groupDTOS 组织集合
     * @param rootGroup 根组织
     * @return
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 14:34
     */
    private List<UserGroupExportDTO> buildExportGroups(List<UserGroupDTO> groupDTOS, UserGroupDTO rootGroup) {
        // 组织Map集合, key值为父节点id
        Map<String, List<UserGroupDTO>> groupMap = groupDTOS.stream()
                .filter(v -> StringUtils.isNotBlank(v.getGroupParentId())).collect(Collectors.groupingBy(UserGroupDTO::getGroupParentId));

        List<UserGroupExportDTO> userGroupExportDTOS = new ArrayList<>(groupDTOS.size());
        List<String> groupNames = new ArrayList<>();
        // 递归设置导出组织集合
        buildExportChildGroups(userGroupExportDTOS, groupMap, groupNames, userGroupConvert.dtoToExport(rootGroup));

        return userGroupExportDTOS;
    }

    /**
     * 递归设置导出组织集合
     *
     * @param userGroupExportDTOS 组织集合
     * @param groupMap            组织map
     * @param groupNames          组织名称集合
     * @param userGroupExportDTO  父级
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 14:34
     */
    private void buildExportChildGroups(List<UserGroupExportDTO> userGroupExportDTOS, Map<String, List<UserGroupDTO>> groupMap,
                                        List<String> groupNames, UserGroupExportDTO userGroupExportDTO) {
        // 设置当前组织: 组织名称集合
        List<String> currentGroupNames = new ArrayList<>(groupNames);
        // 判断是否是隐藏节点,隐藏节点组织名称显示为空
        currentGroupNames.add(UserGroupStatusEnum.ACTIVATED.getStatus().equals(userGroupExportDTO.getGroupStatus())
                ? userGroupExportDTO.getGroupName() : StringUtils.EMPTY);
        userGroupExportDTO.setGroupNames(currentGroupNames);

        // 获取当前组织的子组织集合
        List<UserGroupDTO> childGroups = groupMap.get(userGroupExportDTO.getGroupId());
        // 设置当前组织: 是否有子组织
        userGroupExportDTO.setHaveChildrenFlag(CollectionUtils.isNotEmpty(childGroups)
                ? HaveChildrenFlagEnum.HAVE.getStatus() : HaveChildrenFlagEnum.NOT_HAVE.getStatus());

        // 添加
        userGroupExportDTOS.add(userGroupExportDTO);

        if (HaveChildrenFlagEnum.HAVE.getStatus().equals(userGroupExportDTO.getHaveChildrenFlag())) {
            // 给子组织排序
            childGroups = childGroups.stream()
                    .sorted(Comparator.nullsLast(Comparator.comparing(UserGroupDTO::getGroupOrder, Comparator.nullsLast(Integer::compareTo))))
                    .collect(Collectors.toList());
            for (UserGroupDTO childGroup : childGroups) {
                // 设置当前节点子组织集合
                buildExportChildGroups(userGroupExportDTOS, groupMap, currentGroupNames, userGroupConvert.dtoToExport(childGroup));
            }
        }
    }

    /**
     * 设置动态sheet的数据
     *
     * @param excelWriter     excelWriter
     * @param levelDTOS       层级集合
     * @param groupExportDTOS 组织集合
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 14:34
     */
    private void writeDynamicSheet(ExcelWriter excelWriter, List<UserGroupLevelDTO> levelDTOS, List<UserGroupExportDTO> groupExportDTOS) {
        // 设置要导出的组织用户数据(如果当前组织没有用户,则需导出当前组织,用户数据为空)
        List<UserGroupExportDTO> userGroupExportDTOS = new ArrayList<>();

        for (UserGroupExportDTO userGroupExportDTO : groupExportDTOS) {
            // 只展示生效的组织(隐藏的组织不需要)
            if (!UserGroupStatusEnum.ACTIVATED.getStatus().equals(userGroupExportDTO.getGroupStatus())) {
                continue;
            }

            userGroupExportDTOS.add(userGroupExportDTO);
        }

        Map<String, List<UserGroupExportDTO>> userGroupExportMap = userGroupExportDTOS
                .stream()
                .filter(v -> v.getGroupLevelId() != null)
                .collect(Collectors.groupingBy(UserGroupExportDTO::getGroupLevelId));

        // 设置各个层级(动态)sheet数据
        for (int i = 0; i < levelDTOS.size(); i++) {
            UserGroupLevelDTO levelDTO = levelDTOS.get(i);
            // 设置当前sheet名称等属性
            WriteSheet userGroupExportSheet = EasyExcelFactory
                    .writerSheet(i + 2, levelDTO.getGroupLevelName())
                    .head(UserGroupExportDTO.class)
                    .registerWriteHandler(ExcelUtil.buildGroupStrategy())
                    .registerWriteHandler(new GroupSheetHandler())
                    .build();

            // 设置当前sheet数据
            List<UserGroupExportDTO> exportDTOS = userGroupExportMap.get(levelDTO.getGroupLevelId());
            excelWriter.write(exportDTOS, userGroupExportSheet);
        }
    }

    /**
     * 处理非法层级名称
     * <p>
     * 在重命名工作表时输入的名称无效。请尝试以下操作:
     * 确认输入的名称不多于31个字符。
     * 确认名称中不包含以下字符:: \ / ? * [ 或 ]。
     * 确认名称的第一个或者最后一个字符不能是单引号。
     * 确认工作表名称不为空。
     *
     * @param levelDTO            层级
     * @param illegalLevelNameSeq 自增序列号
     * @return String
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/7/19 11:31
     */
    private int handleIllegalLevel(UserGroupLevelDTO levelDTO, int illegalLevelNameSeq) {
        // 判断层级名称是否为非法工作表名称, 是的话, 替换层级名称
        String levelName = levelDTO.getGroupLevelName();

        if (GroupLevelUtil.checkLevelNameInvalid(levelName)) {
            illegalLevelNameSeq++;
            levelDTO.setGroupLevelName(String.format(BusinessConstant.USER_LEVEL_NAME_ILLEGAL_REPLACE, illegalLevelNameSeq));
            return illegalLevelNameSeq;
        }

        return illegalLevelNameSeq;
    }

    /**
     * 设置首页sheet的动态数据
     *
     * @param excelWriter         excelWriter
     * @param levelDTOS           层级集合
     * @param userGroupExportDTOS 组织集合
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/20 14:34
     */
    private void writeIndexSheet(ExcelWriter excelWriter, List<UserGroupLevelDTO> levelDTOS, List<UserGroupExportDTO> userGroupExportDTOS) {
        // 设置第一个sheet数据
        List<UserGroupExportDTO> firstSheetRows = userGroupExportDTOS.stream()
                .filter(v -> HaveChildrenFlagEnum.NOT_HAVE.getStatus().equals(v.getHaveChildrenFlag())
                        && UserGroupStatusEnum.ACTIVATED.getStatus().equals(v.getGroupStatus()))
                .collect(Collectors.toList());
        List<List<String>> rows = new ArrayList<>(firstSheetRows.size());
        for (UserGroupExportDTO userGroupExportDTO : firstSheetRows) {
            rows.add(userGroupExportDTO.getGroupNames());
        }

        // 设置第一个sheet的动态表头
        List<List<String>> head = new ArrayList<>();
        int illegalLevelNameSeq = 0;
        for (UserGroupLevelDTO levelDTO : levelDTOS) {
            // 处理非法层级名称
            illegalLevelNameSeq = this.handleIllegalLevel(levelDTO, illegalLevelNameSeq);
            head.add(Collections.singletonList(String.format(ExcelConstant.USER_GROUP_EXCEL_REQUIRE_FORMAT, levelDTO.getGroupLevelName())));
        }

        // 设置第一个sheet名称等属性
        WriteSheet sheet = EasyExcelFactory
                .writerSheet(1, ExcelConstant.USER_GROUP_EXCEL_FIRST_SHEET)
                .head(head)
                .registerWriteHandler(ExcelUtil.buildGroupStrategy())
                .registerWriteHandler(new GroupSheetHandler())
                .build();

        excelWriter.write(rows, sheet);
    }

}
